package cn.wolfcode.web.controller;

import cn.wolfcode.common.constants.CommonConstants;
import cn.wolfcode.common.domain.UserInfo;
import cn.wolfcode.common.web.Result;
import cn.wolfcode.common.web.anno.RequireLogin;
import cn.wolfcode.MQ.MQConstant;
import cn.wolfcode.domain.OrderInfo;
import cn.wolfcode.mq.SeckillMessage;
import cn.wolfcode.redis.CommonRedisKey;
import cn.wolfcode.redis.SeckillRedisKey;
import cn.wolfcode.service.IOrderInfoService;
import cn.wolfcode.service.ISeckillProductService;
import cn.wolfcode.util.IdGenerateUtil;
import cn.wolfcode.web.msg.SeckillCodeMsg;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


@RestController
@RequestMapping("/order")
@Slf4j
public class OrderInfoController {


    //本地库存 库存标志位 (key=秒杀id，value=true) 表示该秒杀商品已买完，默认为false
    private static final Map<String, Boolean> STOCK_COUNT_OVER_FLAG = new ConcurrentHashMap<>();


    @Autowired
    private ISeckillProductService seckillProductService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private IOrderInfoService orderInfoService;


    @RequireLogin
    @RequestMapping("/doSeckill")
    public Result<SeckillCodeMsg> doSeckill(Long seckillId, @RequestHeader(CommonConstants.TOKEN_NAME) String token) {

        // 基于 token 获取到用户信息(必须登录)
        UserInfo userInfo = this.getUserByToken(token);

        // 本地缓存校验：判断库存是否充足，减少对redis访问
        Boolean countFlag = STOCK_COUNT_OVER_FLAG.get(seckillId.toString());
        if (countFlag != null && countFlag) {
            return Result.error(SeckillCodeMsg.SECKILL_STOCK_OVER);
        }

        // 3. 判断时间是否大于开始时间 && 小于 开始时间+2小时
//        boolean timeBo = DateUtil.isLegalTime(vo.getStartDate(), time);
//        if (!timeBo) {
//            return Result.error(SeckillCodeMsg.OUT_OF_SECKILL_TIME_ERROR);
//        }

        // redis校验：防止重复提交 seckillOrderHash:phone+seckillId
        String hashOrder = SeckillRedisKey.SECKILL_ORDER_HASH.join(userInfo.getPhone() + ":" + seckillId);
        Long putIfAbsent = redisTemplate.opsForHash().increment(hashOrder, userInfo.getPhone() + "", 1);
        if (putIfAbsent > 1) {
            return Result.error(SeckillCodeMsg.REPEAT_SECKILL);
        }

        // redis校验：预减库存 seckillStockCount:seckillId
        // opsForHash().increment(hash,id,自增) hash自增方法
        String hashCount = SeckillRedisKey.SECKILL_STOCK_COUNT_HASH.join(seckillId + "");
        Long increment = redisTemplate.opsForHash().increment(hashCount, seckillId + "", -1);
        if (increment <= 0) {
            // 本地缓存：库存不足标签设置为true，后续操作直接被拦截，减少redis IO
            STOCK_COUNT_OVER_FLAG.put(seckillId.toString(), true);
            return Result.error(SeckillCodeMsg.SECKILL_STOCK_OVER);
        }

        // 异步下单消息
        long messageId = IdGenerateUtil.get().nextId();
        SeckillMessage message = new SeckillMessage(userInfo.getPhone() + "", seckillId + "",  messageId+ "", token);
        String destinationTopic = MQConstant.SECKILL_ORDER_TOPIC;
        try {
            log.info("步骤一[异步下单]开始，messageId: {}", messageId);
            rocketMQTemplate.asyncSend(destinationTopic, message,new  SendCallback() {

                // 成功回调
                @Override
                public void onSuccess(SendResult sendResult) {
                    log.info("步骤一[异步下单]，异步消息发送成功! msgId: {}, topic: {}, status: {}",
                            sendResult.getMsgId(),
                            destinationTopic,
                            sendResult.getSendStatus());

                }

                // 失败回调
                @Override
                public void onException(Throwable throwable) {
                    log.error("步骤一[异步下单]，异步消息发送失败! topic: {}, error: {}",
                            destinationTopic,
                            throwable.getMessage(),
                            throwable);
                }
            });
            return Result.success(SeckillCodeMsg.SECKILL_QUEUE_ASYNC);

        } catch (Exception e) {
            log.error("步骤一[异步下单]，发送消息到RocketMQ失败", e);

            // 发送失败，需要恢复预减的库存和用户标识
            redisTemplate.opsForHash().delete(hashOrder,userInfo.getPhone() + "");
            redisTemplate.opsForHash().increment(hashCount, seckillId + "", 1);

            return Result.error(SeckillCodeMsg.SECKILL_QUEUE_ASYNC_FAIL);
        }

    }

    private UserInfo getUserByToken(String token) {
        return JSON.parseObject(redisTemplate.opsForValue().get(CommonRedisKey.USER_TOKEN.getRealKey(token)), UserInfo.class);
    }

    @RequestMapping("/find")
    public Result<OrderInfo> findOrderInfoByOrderNo(String orderNo) {

        return Result.success(orderInfoService.find(orderNo));
    }
}
